`timescale 1ps/1ps
module encode_once(
    input   clk
,   input   rst_n
,   input   en
,   input   first
,   input   last
,   output  done
,   input   [127:0] txt
,   input   [127:0] key
,   output  [127:0] out
/* sbox4 port */
,   output [2:0]    cmd
,   output [127:0]  addr
,   input  [31:0]   sbox_out_32
);
reg [127:0] key_reg;

/* 缓存数据 */
always @(posedge clk or negedge rst_n) begin
    if(!rst_n)begin
        key_reg <= 'd0;
    end else if(en & ~first)begin
        key_reg <= key;
    end
end

/* 单次处理状态机 */
parameter   IDLE  = 4'd0,
            SBOX1 = 4'd1,
            SBOX2 = 4'd2,
            SBOX3 = 4'd3,
            SBOX4 = 4'd4,
            BACK1 = 4'd5,
            BACK2 = 4'd6,
            BACK3 = 4'd7,
            BACK4 = 4'd8,
            OUT   = 4'd9;

reg[3:0] cur_state;
reg[3:0] nxt_state;

always @(posedge clk or negedge rst_n) begin
    if(!rst_n)
        cur_state <= IDLE;
    else
        cur_state <= nxt_state;
end

always @(*) begin
    case(cur_state)
        IDLE :  
            if(first)
                nxt_state = OUT;
            else if(en)
                nxt_state = SBOX1;
            else
                nxt_state = IDLE;
        SBOX1: nxt_state = SBOX2;
        SBOX2: nxt_state = SBOX3;
        SBOX3: nxt_state = SBOX4;
        SBOX4: nxt_state = BACK1;
        BACK1: nxt_state = BACK2;
        BACK2: nxt_state = BACK3;
        BACK3: nxt_state = BACK4;
        BACK4: nxt_state = OUT;
        OUT  : nxt_state = IDLE;
        default:nxt_state = IDLE;
    endcase
end

reg [31:0] A [0:3];
reg [31:0] B [0:3];
wire [31:0] back_out_32;

always @(posedge clk or negedge rst_n) begin
    if(!rst_n) begin
        A[0] <= 'd0;B[0] <= 'd0;
        A[1] <= 'd0;B[1] <= 'd0;
        A[2] <= 'd0;B[2] <= 'd0;
        A[3] <= 'd0;B[3] <= 'd0;
    end else if(en && ~first) begin
        A[0] <= txt[127:96];
        A[1] <= txt[ 95:64];
        A[2] <= txt[ 63:32];
        A[3] <= txt[ 31: 0];
    end else if (cur_state == SBOX1) begin
        B[0] <= sbox_out_32;
    end else if (cur_state == SBOX2) begin
        B[1] <= sbox_out_32;
    end else if (cur_state == SBOX3) begin
        B[2] <= sbox_out_32;
    end else if (cur_state == SBOX4) begin
        B[3] <= sbox_out_32;
    end else if (cur_state == BACK1) begin
        A[0] <= back_out_32;
    end else if (cur_state == BACK2) begin
        A[1] <= back_out_32;
    end else if (cur_state == BACK3) begin
        A[2] <= back_out_32;
    end else if (cur_state == BACK4) begin
        A[3] <= back_out_32;
    end
end

reg [1:0]sel;
always @(*) begin
    case(cur_state)
        SBOX1:sel = 2'b00;
        SBOX2:sel = 2'b01;
        SBOX3:sel = 2'b10;
        SBOX4:sel = 2'b11;
        default:sel = 'd0;
    endcase
end

reg [1:0]j;
always @(*) begin
    case(cur_state)
        BACK1:j = 2'b00;
        BACK2:j = 2'b01;
        BACK3:j = 2'b10;
        BACK4:j = 2'b11;
        default:j = 'd0;
    endcase
end

reg last_reg;

always @(posedge clk or negedge rst_n) begin
    if(!rst_n)
        last_reg <= 'd0;
    else if(en & last)
        last_reg <= 1'b1;
    else if(cur_state == IDLE)
        last_reg <= 1'b0;
end

/* sbox */
assign cmd  = {1'b1,sel} ;
assign addr = {A[0],A[1],A[2],A[3]};

en_backend  u_en_backend (
    .txt                     ( {B[0],B[1],B[2],B[3]}),
    .key                     ( key_reg              ),
    .j                       ( j                    ),
    .last                    ( last_reg             ),

    .out                     ( back_out_32          )
);

assign done = cur_state == OUT;
assign out = first ? key ^ txt : {A[0],A[1],A[2],A[3]};
endmodule